package xpoi.yangda.xin;

import com.alibaba.fastjson.JSONObject;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import xpoi.yangda.xin.model.DataSource;
import xpoi.yangda.xin.model.DataSourceField;
import xpoi.yangda.xin.util.WordDefined;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 根据特定Excel导出Excel文档
 *
 * @author izifeng
 * @version 创建时间 2018/8/30 11:39
 * @site https://gitee.com/izifeng/XPOI.git
 */
public abstract class BaseWorkbookDesigner {
    /**
     * 工作表
     */
    protected HSSFWorkbook workBook;

    /**
     * 文档输出流
     */
    protected ByteArrayOutputStream outStream;

    /**
     * 单个数据源
     */
    protected DataSource dataSource;

    /**
     * 多个数据源
     */
    protected List<DataSource> dataSourceList;

    public BaseWorkbookDesigner() {
        outStream = new ByteArrayOutputStream();
    }

    public ByteArrayOutputStream getOutStream() {
        return outStream;
    }

    /**
     * 打开excel模板
     *
     * @param filePath excel模板路径
     * @throws IOException
     */
    public void open(String filePath) throws IOException {
        if (filePath.trim().toLowerCase().endsWith(WordDefined.EXCEL_IMPORT_SUFFIX)) {
            File file = new File(filePath);

            FileInputStream fis = new FileInputStream(file);

            POIFSFileSystem fs = new POIFSFileSystem(fis);

            // 读取excel模板
            workBook = new HSSFWorkbook(fs);
        } else {
            throw new IllegalArgumentException(WordDefined.ONLY_SUPPORT_XLS);
        }
    }

    /**
     * 打开excel模板
     *
     * @throws IOException
     */
    public void open(InputStream inputStream) throws IOException {
        POIFSFileSystem fs = new POIFSFileSystem(inputStream);
        // 读取excel模板
        workBook = new HSSFWorkbook(fs);
    }

    /**
     * 打开excel模板
     *
     * @throws IOException
     */
    public void open(File file) throws IOException {
        POIFSFileSystem fs = new POIFSFileSystem(file);
        // 读取excel模板
        workBook = new HSSFWorkbook(fs);
    }

    /**
     * 添加数据源
     *
     * @param dataSource
     */
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 添加数据源
     *
     * @param dataSourceList
     */
    public void setDataSource(List<DataSource> dataSourceList) {
        this.dataSourceList = dataSourceList;
    }

    /**
     * 数据处理
     *
     * @throws IOException
     */
    public abstract void process() throws IOException;

    /**
     * 写自定义字段试数据
     *
     * @param sheet      工作簿
     * @param cdsField   工作簿中自定义字段位置信息
     * @param dataSource 数据源
     */
    public void writeCustomField(HSSFSheet sheet, Map<String, DataSourceField> cdsField, DataSource dataSource) {
        Map<String, String> customField = dataSource.getFieldMap();
        for (Map.Entry<String, DataSourceField> entry : cdsField.entrySet()) {
            String key = entry.getKey();
            DataSourceField dsf = entry.getValue();

            if (!customField.containsKey(key)) {
                continue;
            }

            // 获取行
            Row row = sheet.getRow(dsf.getRowIdx());
            // 获取列
            Cell cell = row.getCell(dsf.getColIdx());
            // 填值
            cell.setCellValue(customField.get(key));
        }
    }

    /**
     * 写数据源字段数据
     *
     * @param sheet      工作簿
     * @param dsField    工作簿中数据源字段位置信息
     * @param dataSource 数据源
     */
    public void writeDataSource(HSSFSheet sheet, Map<String, DataSourceField> dsField, DataSource dataSource) {
        List<JSONObject> ds = dataSource.getDsList();
        for (Map.Entry<String, DataSourceField> entry : dsField.entrySet()) {
            String key = entry.getKey();
            DataSourceField sdf = entry.getValue();
            for (int i = 0; i < ds.size(); i++) {
                JSONObject json = ds.get(i);
                if (!json.containsKey(key)) {
                    continue;
                }

                // 获取行
                Row row = sheet.getRow(sdf.getRowIdx() + i);
                if (row == null) {
                    row = sheet.createRow(sdf.getRowIdx() + i);
                }

                // 获取列
                Cell cell = row.getCell(sdf.getColIdx());
                if (cell == null) {
                    cell = row.createCell(sdf.getColIdx());
                }

                // 设置单元格样式
                cell.setCellStyle(sdf.getCellStyle());

                // 填充单元格数据
                cell.setCellValue(json.getString(key));
            }
        }
    }

    /**
     * 获取工作簿
     *
     * @param index 工作簿索引
     * @return
     */
    protected HSSFSheet getHSSFSheet(int index) {
        return workBook.getSheetAt(index);
    }

    /**
     * 提取模板中数据源相关字段位置信息，包括：行、列、单元格样式
     *
     * @param sheet 工作簿
     * @param map1  自定义字段集
     * @param map2  数据源字段集
     */
    public void getDataSourceField(HSSFSheet sheet, Map<String, DataSourceField> map1, Map<String, DataSourceField> map2) {
        // 获得总列数
        int coloumNum = sheet.getRow(0).getPhysicalNumberOfCells();

        // 获得总行数
        int rowNum = sheet.getLastRowNum();

        // 提取模板中数据源相关字段位置信息，包括：行、列、单元格样式
        for (int rowIdx = 0; rowIdx <= rowNum; rowIdx++) {
            Row row = sheet.getRow(rowIdx);
            for (int colIdx = 0; colIdx < coloumNum; colIdx++) {
                // 获取单元格
                Cell cell = row.getCell(colIdx);
                if (cell == null) {
                    continue;
                }

                // 单元格值
                String cellValue = cell.getStringCellValue().trim();

                // 单元格内容不满足模板要求
                if (cellValue.length() == 0 || cellValue.indexOf("&=") != 0) {
                    continue;
                }

                DataSourceField dsf = new DataSourceField(rowIdx, colIdx, cell.getCellStyle());

                if (cellValue.indexOf("&=$") == 0) {
                    // 自定义数据源字段
                    String field = cellValue.replace("&=$", "");
                    map1.put(field, dsf);
                } else if (cellValue.indexOf("&=") == 0) {
                    // 数据源字段
                    String field = cellValue.replace("&=", "");
                    map2.put(field, dsf);
                }
            }
        }
    }

    /**
     * map list 转 JSONObject
     *
     * @param list
     * @param <E>
     * @return
     */
    public <E> List<JSONObject> listMapToJSONObject(List<E> list) {
        List<JSONObject> dsList = new ArrayList<>(list.size());
        for (Object object : list) {
            Map<String, Object> map = (Map<String, Object>) object;
            JSONObject jsonObj = new JSONObject();
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                jsonObj.put(entry.getKey(), entry.getValue());
            }
            dsList.add(jsonObj);
        }
        return dsList;
    }

    /**
     * 保存文件
     *
     * @param filePath 目标文件路径
     * @throws IOException
     */
    public void saveFile(String filePath) {
        // 获取流字节
        byte[] content = outStream.toByteArray();

        FileOutputStream outputStream = null;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            // 将流字节转换成输入流
            InputStream is = new ByteArrayInputStream(content);

            // 获取目标文件输出流
            outputStream = new FileOutputStream(filePath);

            // 获取缓冲字节输入流
            bis = new BufferedInputStream(is);

            // 获取缓冲字节输出流
            bos = new BufferedOutputStream(outputStream);

            byte[] buff = new byte[8192];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (bis != null) {
                    bis.close();
                }

                if (bos != null) {
                    bos.close();
                }

                if (outputStream != null) {
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
